/*
 * Copyright (C) 2009 Foregone.
 * All Rights Reserved. The following is Source Code and is subject to all restrictions
 * on such code as contained in the End User License Agreement accompanying this product.
 */

package org.foregone.kwicks.kwicksMenu;

import com.google.gwt.animation.client.Animation;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * User: Thil Bandara
 * Date: Oct 13, 2009
 * Version: 0.7
 * - Add Mouse handlers to all kwicksitems
 * - Handle animations for mouse over and out
 * <p/>
 */
public class KwicksList extends ComplexPanel {

  private KwicksMouseHandler kwicksMouseHandler = new KwicksMouseHandler();
  private KwicksAnimation kwicksAnimation = new KwicksAnimation();
  private final double MAX_SIZE = 1.5;
  private double minSize;
  private int animationTime = 1000;
  private int backgroundOffsetX = 10;
  private int backgroundOffsetY = 0;

  public KwicksList() {
    Element base = DOM.createElement("ul");
    base.setClassName("kwicks-ul");
    setElement(base);
  }


  /**
   * Set the element background offset values
   * @param x background x position offset from initial width
   * @param y background y position offset
   */
  public void setBackgroundOffset( int x, int y ){
    this.backgroundOffsetX = x;
    this.backgroundOffsetY = y;
  }


  /**
   *
   * @param animationTime during of animations
   */
  public void setAnimationTime(int animationTime) {
    this.animationTime = animationTime;
  }

  /**
   * @param w {@link Widget} widget to add to this element
   */
  @Override
  public void add(Widget w) {
    super.add(w, getElement());
  }

  /**
   * Get and store the initial widths of the elements
   * Get the min percentage we will reduce the elements to
   */
  @Override
  protected void onLoad() {
    super.onLoad();
    for (Widget w : getChildren()) {
      KwicksItem kwi = (KwicksItem) w;
      if (kwi.initialWidth == 0) {
        kwi.initialWidth = kwi.getElement().getClientWidth();
        kwi.setBackgroundImage( backgroundOffsetX, backgroundOffsetY );
      }
    }
    minSize = (getWidgetCount() - MAX_SIZE) / (getWidgetCount() - 1);
  }

  /**
   * Add the mouse handlers to all incoming kwicksitems and all to list
   * Calculate the min retracting percentage
   *
   * @param kwicksItem {@link KwicksItem} item to add to list
   */
  public void addKwicksItem(KwicksItem kwicksItem) {
    kwicksItem.addMouseOutHandler(kwicksMouseHandler);
    kwicksItem.addMouseOverHandler(kwicksMouseHandler);
    kwicksItem.position = getWidgetCount();
    if (kwicksItem.linkURL != null) {
      kwicksItem.addClickHandler(kwicksMouseHandler);
    }

    this.add(kwicksItem);
  }

  /**
   * Mouse handlers to run animation
   */
  public class KwicksMouseHandler implements MouseOverHandler, MouseOutHandler, ClickHandler {

    /**
     * Cancel out the current animation and set off a non hover animation
     * @param event {@link MouseOutEvent}  occurring for the element
     */
    public void onMouseOut(MouseOutEvent event) {
      kwicksAnimation.cancel();
      kwicksAnimation.run(animationTime, Boolean.FALSE);
    }

    /**
     * Start hover animation
     * @param event {@link MouseOverEvent} occurring for the element
     */
    public void onMouseOver(MouseOverEvent event) {
      kwicksAnimation.position = ((KwicksItem) event.getSource()).position;
      kwicksAnimation.run(animationTime, Boolean.TRUE);
    }

    /**
     * If any link url is specified will link to that url
     * @param event the {@link ClickEvent} that was fired
     */
    public void onClick(ClickEvent event) {
      KwicksItem kwicksItem = (KwicksItem) event.getSource();
      if (kwicksItem.linkURL.startsWith("http://")) {
        Window.Location.replace(kwicksItem.linkURL);
      } else {
        Window.Location.assign(kwicksItem.linkURL);
      }
    }
  }

  /**
   * Animation to handle kwicks hovers
   */
  public class KwicksAnimation extends Animation {
    int position;
    boolean hover;

    /**
     * Wrapper for run method to allocate hover
     *
     * @param time  value of animation length
     * @param hover if mouse is hovering
     */
    public void run(int time, boolean hover) {
      this.hover = hover;
      super.run(time);
    }

    /**
     * Cancel any animation 
     */
    @Override
    protected void onCancel() {
    }

    /**
     * Update the item widths of the element
     *
     * @param p current progress
     */
    @Override
    protected void onUpdate(double p) {
      double progress;
      for (Widget widget : getChildren()) {
        KwicksItem kmi = (KwicksItem) widget;
        int targetWidth = kmi.initialWidth;

        if (kmi.position != position) {
          progress = (1 - Math.pow(1 - p, 1));
          if( hover ){
            progress = (1 - Math.pow(1 - p, 2));
            targetWidth *= minSize;
          }
        }else {
          progress = (1 - Math.pow(1 - p, 2));
          if( hover ){
            progress = (1 - Math.pow(1 - p, 1));
            targetWidth *= MAX_SIZE;
          }
        }

        int diff = targetWidth - kmi.getElement().getClientWidth();
        int diffWidth = (int) Math.rint(diff * progress);

        int newWidth = kmi.getElement().getClientWidth() + diffWidth;

        if (newWidth < (int) (minSize * kmi.initialWidth)) {
          newWidth = (int) (minSize * kmi.initialWidth);
        } else if (newWidth > (int) (MAX_SIZE * kmi.initialWidth)) {
          newWidth = (int) (MAX_SIZE * kmi.initialWidth);
        }
        kmi.setWidth(newWidth + "px");
      }
    }
  }

}
